Next | Prev | Up | Top | Contents | Index
Entry Point intr()
The prototype of the pfxintr() entry point is
void pfxintr(int ivec);
The ivec argument is a number that represents the interrupt, depending on the type of device, type of bus, and the way the interrupt was associated to the driver.
When an interrupt occurs, the system is in an unknown state. As a result, the interrupt handler can use only a restricted set of kernel services, and no services that can sleep. In general, the interrupt handler implements the following tasks.
- When the driver supports multiple logical units, use ivec to locate the data structure for the interrupting unit.
- Determine the reason for the interrupt by interrogating the device.
- When the interrupt is a response to a device operation, note the success or failure of the command.
- If the driver top half is waiting for the interrupt, waken it.
- If the driver supports polling, and the interrupt represents a pollable event, call pollwakeup().
- If the device is not in an error state and another operation is waiting to be started, start it.
The details of each of these tasks depends on the hardware and on the design of the data structures used by the driver top half.
Mutual Exclusion
In a uniprocessor system, there is only one CPU and when it is executing the interrupt handler, nothing else is executing. An interrupt handler can only be preempted by an interrupt of higher priority--which would be an interrupt for a different driver, and so would have no conflicts with this driver over the use of data.
In a multiprocessor, an interrupt can be taken on any CPU, while other CPUs continue to execute kernel or user code. (In the Challenge and Onyx systems, interrupts are "sprayed" to CPUs in rotation to equalize the workload. Also, VME interrupts can be directed to specific CPUs for handling; see "Using the IPL Statement".)
In a multiprocessor, when an interrupt must be handled by a driver that is not marked as multiprocessor-aware (see "Flag D_MP"), the interrupt may be received on some other CPU, but the driver interrupt entry point is always executed on CPU 0.
In a multiprocessor, when the driver is multiprocessor-aware, one or more other CPUs can execute in the driver's top-half entry points while another CPU executes the driver's interrupt entry point. An interrupt handler written for a multiprocessor must not assume that it has exclusive use of the driver's data (see "Planning for Multiprocessor Use").
It is theoretically possible in a multiprocessor for a device to interrupt; for one CPU to enter the interrupt handler; and for the device to interrupt again, resulting in multiple concurrent entries to the same interrupt handler. However, IRIX prevents this. You can assume that your interrupt handler code is entered serially, and not used concurrently by multiple CPUs.
Performance and Latency
Speed in exiting the interrupt handler is critical to system performance. In a uniprocessor, the system is doing nothing else while it executes the handler, and it cannot respond to interrupts of a lower priority. In a multiprocessor, interrupts can be taken by different CPUs. While a CPU executes a handler, that CPU cannot respond to lower-priority interrupts, but other CPUs can be processing user-level code or responding to other interrupts.
Completing Block I/O
In a block device driver, an I/O operation is represented by the buf_t structure. The pfxstrategy() routine starts operations and waits for them to complete (see "Entry Point strategy()").
The interrupt entry point sets the residual count in b_resid. It can post an error using bioerror(). It posts the operation complete and wakens the pfxstrategy() routine by calling biodone(). If the pfxstrategy() entry has set the address of a completion callback function in the b_iodone field of the buf_t, biodone() invokes it. (For more discussion, see "Waiting for Block I/O to Complete".)
Completing Character I/O
In a character device driver, the driver top half typically awaits an interrupt by sleeping on a semaphore or synchronizing variable, and the interrupt routine posts the semaphore (see "Waiting for a General Event"). Error information must be passed in driver variables according to some local convention.
Calling pollwakeup()
When the interrupt represents an event that can be reported by the driver's pfxpoll() entry point (see "Entry Point poll()"), the interrupt handler must report the event to the kernel, in case some user process is waiting in a poll() call. Hypothetical code to do this is shown in Example 8-4.
Example 8-4 : Hypothetical Call to pollwakeup()
hypo_intr(int ivec)
{
struct hypo_dev_info *pinfo;
if (! pinfo = find_dev_info(ivec))
return; /* not our device */
...
if (pinfo->have_data_flag)
pollwakeup (pinfo->phead, POLLIN, POLLRDNORM);
if (pinfo->output_ok_flag)
pollwakeup (pinfo->phead, POLLOUT);
...
}
Next | Prev | Up | Top | Contents | Index